﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Globalization;
using System.Text.RegularExpressions;

namespace SDF
{
    [TypeConverter(typeof(FigureWidthConverter))]
    public struct FigureWidth : IEquatable<FigureWidth>
    {
        #region Constructors

        public FigureWidth(double pixels)
            : this(pixels, false)
        {
        }

        public FigureWidth(double value, bool isRelativeToParagraph):this()
        {
            if (double.IsInfinity(value))
                throw new ArgumentException("FigureWidth.Value cannot be set to infinity!");

            if (value < 0.0)
                throw new ArgumentOutOfRangeException("FigureWidth.Value must be a positive number!");

            if (isRelativeToParagraph)
            {
                if (value > 1)
                    throw new ArgumentOutOfRangeException("FigureWidth.Value must be between 0 and 1 when relative to the paragraph's width!");
            }
            else
            {
                if (value > Paragraph.MaximumWidth)
                    throw new ArgumentOutOfRangeException(String.Format("FigureWidth.Value must be less than {0}.", Paragraph.MaximumWidth));
            }

            Value = value;
            IsRelativeToParagraph = isRelativeToParagraph;
        }

        #endregion

        #region Properties

        public double Value { get; private set; }

        public bool IsRelativeToParagraph { get; private set; }

        public bool IsAuto
        {
            get { return double.IsNaN(Value); }
        }

        #endregion

        #region Operators

        public static bool operator ==(FigureWidth a, FigureWidth b)
        {
            return ((a.IsRelativeToParagraph == b.IsRelativeToParagraph) && (a.Value == b.Value));
        }

        public static bool operator !=(FigureWidth a, FigureWidth b)
        {
            if (a.IsRelativeToParagraph == b.IsRelativeToParagraph)
            {
                return (a.Value != b.Value);
            }
            return true;
        }

        #endregion

        #region IEquatable Methods

        public override bool Equals(object oCompare)
        {
            if (oCompare is FigureWidth)
            {
                FigureWidth width = (FigureWidth)oCompare;
                return (this == width);
            }
            return false;
        }

        public bool Equals(FigureWidth figureLength)
        {
            return (this == figureLength);
        }

        public override int GetHashCode()
        {
            if (IsRelativeToParagraph)
                return -1 * (int)this.Value;
            else
                return (int)this.Value;
        }

        #endregion

        #region ToString Method

        public override string ToString()
        {
            if (IsAuto)
                return "Auto";

            if (!IsRelativeToParagraph)
                return Value.ToString();

            return String.Format("{0} %", Value * 100.0);
        }

        public static bool TryParse(string s, out FigureWidth result)
        {
            result = FigureWidth.Auto;
            if (s == null)
                return false;

            if (s.ToLower() == "auto")
                return true;

            if (!Regex.IsMatch(s, @"^\d+(\.(\d)+)?((\x20)?%)?$"))
                return false;

            bool isRelativeToParagraph = false;
            if (s.EndsWith("%"))
            {
                isRelativeToParagraph = true;
                s = s.Substring(0, s.Length - 1).Trim();
            }

            double value;
            if (!double.TryParse(s, out value))
                return false;

            result = new FigureWidth(value, isRelativeToParagraph);
            return true;
        }

        #endregion

        #region Constants

        public static readonly FigureWidth Auto = new FigureWidth(double.NaN);

        #endregion
    }


}
